/*
 Copyright 2008 Under Dusken

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

package org.dyndns.anderlin.jmplayer.dao.impl;

import org.springframework.dao.DataAccessException;
import org.springframework.orm.jpa.support.JpaDaoSupport;
import org.springframework.orm.jpa.JpaTemplate;
import org.dyndns.anderlin.jmplayer.dao.GenericDao;

import javax.persistence.Query;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * This class implements GenericDao, but does not add a type. Type may be added
 * by subclasses, or by Spring configuration. Everything that are common to all
 * JPA Daos have been added here. Type-specific tasks, like getting object by ID
 * are delegated to subclasses. No methods are marked final, so it is possible
 * for subclasses to remove some functionality if needed/required.
 *
 * @author Erlend Hamnaberg<erlenha@underdusken.no>
 */
@SuppressWarnings("unchecked")
public class GenericJpaDao extends JpaDaoSupport implements GenericDao {

    public <T, PK extends Serializable> T getByID(Class<T> type, PK ID) throws DataAccessException {
        return getJpaTemplate().find(type, ID);
    }

    public <T, PK extends Serializable> T getReference(Class<T> type, PK ID) throws DataAccessException {
        return getJpaTemplate().getReference(type, ID);
    }

    public <T> List<T> getByQuery(String query, Map<String, ?> map)
            throws DataAccessException {
        if (map != null) {
            return getJpaTemplate().findByNamedParams(query, map);
        } else {
            return getJpaTemplate().find(query);
        }
    }

    public <T> List<T> getByQuery(String query, Map<String, ?> map,
                              int offset, int size) throws DataAccessException {
        Query q = getJpaTemplate().getEntityManagerFactory()
                .createEntityManager().createQuery(query);
        if (map != null) {
            Set<? extends Map.Entry<String, ?>> set = map
                    .entrySet();
            for (Map.Entry me : set) {
                q.setParameter((String) me.getKey(), me.getValue());
            }
        }
        q.setFirstResult(offset);
        q.setMaxResults(size);
        return q.getResultList();
    }

    public <T> List<T> getByNamedQuery(String query,
                                   Map<String, ?> map) throws DataAccessException {
        if (map != null) {
            return getJpaTemplate().findByNamedQueryAndNamedParams(query, map);
        } else {
            return getJpaTemplate().findByNamedQuery(query);
        }
    }

    public Integer getNumberByNamedQuery(String query, Map<String, ?> map) throws DataAccessException {
        // TODO: FIX THIS!! Should be better documented, and possibly much faster
        List list;
        if (map != null) {
            list = getJpaTemplate().findByNamedParams(query, map);
        } else {
            list = getJpaTemplate().findByNamedQuery(query);
        }
        if (list == null || list.size() < 1) {
            return 0;
        } else {
            Long n = (Long) list.get(0);
            return n.intValue();
        }
    }

    public <T> List<T> getByNamedQuery(String query,
                                   Map<String, ?> map, int offset, int size)
            throws DataAccessException {
        Query q = getJpaTemplate().getEntityManagerFactory()
                .createEntityManager().createNamedQuery(query);
        if (map != null) {
            Set<? extends Map.Entry<String, ?>> set = map
                    .entrySet();
            for (Map.Entry me : set) {
                q.setParameter((String) me.getKey(), me.getValue());
            }
        }
        q.setFirstResult(offset);
        q.setMaxResults(size);
        return q.getResultList();
    }

    public <T> void remove(T object) throws DataAccessException {
        JpaTemplate em = getJpaTemplate();
        Object obj = em.contains(object) ? object : em.merge(object);
        em.remove(obj);
    }

    /**
     * Does some JPA specific operations
     *
     * @param object to be saved or updated
     * @return persistent instance
     * @throws DataAccessException
     * @see javax.persistence.EntityManager#merge
     */
    public <T> T saveOrUpdate(T object) throws DataAccessException {
        logger.debug("saveOrUpdate called with object [" + object.getClass().getSimpleName() + "]: " + object);
        return getJpaTemplate().merge(object);
    }
}
